package com.tanhua.server.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.tanhua.autoconfig.template.HuanXinTemplate;
import com.tanhua.commons.utils.Constants;
import com.tanhua.dubbo.api.*;
import com.tanhua.model.domain.Question;
import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.dto.RecommendUserDto;
import com.tanhua.model.mongo.RecommendUser;
import com.tanhua.model.mongo.UserLocation;
import com.tanhua.model.mongo.Visitors;
import com.tanhua.model.vo.ErrorResult;
import com.tanhua.model.vo.NearUserVo;
import com.tanhua.model.vo.PageResult;
import com.tanhua.model.vo.TodayBest;
import com.tanhua.server.exception.BusinessException;
import com.tanhua.server.interceptor.UserHolder;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author: cl
 * @Date: 2022/5/3 - 05 - 03
 * @Description:
 */

/**
 * 缘分匹配用户模块
 */
@Service
public class TanhuaService {

    //调用缘分值服务
    @DubboReference
    private RecommendUserApi recommendUserApi;

    //用户信息服务
    @DubboReference
    private UserInfoApi userInfoApi;

    //问题服务
    @DubboReference
    private QuestionApi questionApi;

    //环信
    @Autowired
    private HuanXinTemplate huanXinTemplate;

    //配置文件默认推荐用户
    @Value("${tanhua.default.recommend.users}")
    private String recommendUser;

    //喜欢和不喜欢关系服务
    @DubboReference
    private UserLikeApi userLikeApi;

    //redis服务
    @Autowired
    private RedisTemplate<String, String> redis;

    //消息模块
    @Autowired
    private MessagesService messagesService;

    //地理位置服务
    @DubboReference
    private UserLocationApi userLocationApi;

    //访客服务
    @DubboReference
    private VisitorsApi visitorsApi;

    /**
     * 今日佳人
     *
     * @return
     */
    public TodayBest toDayBest() {

        //1、获取用户id
        Long userId = UserHolder.getUserId();
        //2、查询最高缘分分
        RecommendUser recommendUser = recommendUserApi.queryWithMaxScore(userId);
        if (recommendUser == null) {//如果无数据，给定初始数据
            recommendUser = new RecommendUser();
            recommendUser.setUserId(1l);
            recommendUser.setScore(99d);
        }
        //3、获取最高缘分的用户id
        Long maxId = recommendUser.getUserId();
        //4、查询最高缘分用户信息
        UserInfo user = userInfoApi.findById(maxId);
        //5、封装vo对象返回
        TodayBest todayBest = TodayBest.init(user, recommendUser);
        return todayBest;
    }

    /**
     * 推荐缘分用户分页展示
     *
     * @param recommendUserDto
     * @return
     */
    public PageResult recommendation(RecommendUserDto recommendUserDto) {

        //1、获取用户id
        Long userId = UserHolder.getUserId();
        //2、查询缘分匹配列表
        Integer page = recommendUserDto.getPage();
        Integer pagesize = recommendUserDto.getPagesize();
        PageResult pageResult = recommendUserApi.queryRecommendUserList(page, pagesize, userId);

        //3、查询匹配用户信息
        List<RecommendUser> items = (List<RecommendUser>) pageResult.getItems();

        //4、判断是否有匹配的用户
        if (items == null || items.size() <= 0) {
            return pageResult;
        }

        //5、使用工具类获取匹配的用户id
        List<Long> ids = CollUtil.getFieldValues(items, "userId", Long.class);

        //6、调用userInfo批量查询用户信息
        Map<Long, UserInfo> map = userInfoApi.selectByIds(ids, recommendUserDto);
        //7、把数据放入返回vo分页对象(集合中放入每日推荐对象类)
        List<TodayBest> list = new ArrayList<>();
        for (RecommendUser item : items) {
            UserInfo userInfo = map.get(item.getUserId());
            if (userInfo != null) {
                list.add(TodayBest.init(userInfo, item));
            }
        }
        //8、封装page对象并返回
        pageResult.setItems(list);
        return pageResult;
    }

    /**
     * 通过佳人id查询佳人信息(并保存访客信息)
     *
     * @param userId
     * @return
     */
    public TodayBest getPersonalInfo(Long userId) {
        //1、获取用户id
        Long toUserId = UserHolder.getUserId();
        //2、查询佳人信息
        UserInfo userInfo = userInfoApi.findById(userId);
        //3、查询用户对应的佳人缘分值
        RecommendUser recommendUser = recommendUserApi.findByUserIdAndToUserId(userId, toUserId);

        //保存访客信息
        Visitors visitors = new Visitors();
        visitors.setUserId(userId);
        visitors.setVisitorUserId(UserHolder.getUserId());
        visitors.setDate(System.currentTimeMillis());
        visitors.setFrom("首页");
        visitors.setVisitDate(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        visitors.setScore(recommendUser.getScore());//分数
        visitorsApi.save(visitors);


        //4、封装返回
        return TodayBest.init(userInfo, recommendUser);
    }

    /**
     * 通过用户id查询用户问题
     *
     * @param userId
     * @return
     */
    public String getQusetion(Long userId) {

        Question q = questionApi.findByUserId(userId);
        return q == null ? "你喜欢什么？" : q.getTxt();
    }

    /**
     * 根据接收方的id发送信息
     *
     * @param userId
     * @param reply
     */
    public void sendMessage(Long userId, String reply) {

        //1、获取发送方的id,并查询发送方的用户信息
        Long userId1 = UserHolder.getUserId();//发送方的id
        UserInfo userInfo = userInfoApi.findById(userId1);
        //2、构建发送的josn字符串
        Map map = new HashMap();
        map.put("userId", userId1);//发送方id
        map.put("huanXinId", Constants.HX_USER_PREFIX + userId1);//发送方环信id
        map.put("nickname", userInfo.getNickname());//发送方昵称
        map.put("strangerQuestion", getQusetion(userId));//接受方的问题
        map.put("reply", reply);//发送的内容
        String s = JSON.toJSONString(map);
        //3、利用环信发送消息
        Boolean aBoolean = huanXinTemplate.sendMsg(Constants.HX_USER_PREFIX + userId, s);
        //1、接受方的环信id，2、消息
        if (!aBoolean) {
            throw new BusinessException(ErrorResult.error());
        }
    }

    /**
     * 获取选择喜欢或不喜欢的列表
     *
     * @return
     */
    public List<TodayBest> getLikeList() {

        //1、调用服务获取推荐的用户(要排除喜欢或者不喜欢的用户，并且限制数量为10条)
        List<RecommendUser> list = recommendUserApi.queryCardsList(UserHolder.getUserId(), 10);
        //2、如果没有推荐的用户就用配置文件自定义的用户
        if (CollUtil.isEmpty(list)) {
            list = new ArrayList<>();
            String[] userIds = recommendUser.split(",");
            for (String userId : userIds) {
                RecommendUser recommendUser = new RecommendUser();
                recommendUser.setUserId(Convert.toLong(userId));
                recommendUser.setToUserId(UserHolder.getUserId());
                recommendUser.setScore(RandomUtil.randomDouble(60, 90));
                list.add(recommendUser);
            }
        }
        //3、封装vo对象返回
        List<Long> ids = CollUtil.getFieldValues(list, "userId", Long.class);
        Map<Long, UserInfo> map = userInfoApi.selectByIds(ids, new RecommendUserDto());
        List<TodayBest> vos = new ArrayList<>();
        for (RecommendUser user : list) {
            if (map.get(user.getUserId()) != null) {
                TodayBest init = TodayBest.init(map.get(user.getUserId()), user);
                vos.add(init);
            }
        }
        return vos;
    }

    /**
     * 右滑喜欢好友
     *
     * @param likeUserId
     */
    public void love(Long likeUserId) {
        //1、添加到数据库喜欢关系(喜欢和不喜欢共用)
        boolean re = userLikeApi.loveOrUnlove(UserHolder.getUserId(), likeUserId, true);
        if (!re) {
            throw new BusinessException(ErrorResult.error());
        }
        //2、添加到redis中喜欢关系(用set结构,并删除redis中不喜欢保存得的信息)
        redis.opsForSet().add(Constants.USER_LIKE_KEY + UserHolder.getUserId(), likeUserId.toString());
        redis.opsForSet().remove(Constants.USER_NOT_LIKE_KEY + UserHolder.getUserId(), likeUserId.toString());
        //3、判断是否相互喜欢
        Boolean b = redis.opsForSet().isMember(Constants.USER_LIKE_KEY + likeUserId, UserHolder.getUserId().toString());
        //4、相互喜欢添加双向好友
        if (b) {
            //调用message模块添加好友(添加到环信，再添加到数据库)
            messagesService.contacts(likeUserId);
        }
    }

    /**
     * 左滑动不喜欢
     *
     * @param likeUserId
     */
    public void unLove(Long likeUserId) {

        //1、添加到数据库喜欢关系(喜欢和不喜欢共用)
        boolean re = userLikeApi.loveOrUnlove(UserHolder.getUserId(), likeUserId, false);
        if (!re) {
            throw new BusinessException(ErrorResult.error());
        }
        //2、添加到redis中不喜欢关系(用set结构,并删除redis中喜欢保存得的信息)
        redis.opsForSet().add(Constants.USER_NOT_LIKE_KEY + UserHolder.getUserId(), likeUserId.toString());
        redis.opsForSet().remove(Constants.USER_LIKE_KEY + UserHolder.getUserId(), likeUserId.toString());
        //3、判断是否相互不喜欢
        Boolean b = redis.opsForSet().isMember(Constants.USER_NOT_LIKE_KEY + likeUserId, UserHolder.getUserId().toString());
        //4、相互不喜欢删除双向好友
        if (b) {

            //调用message服务删除好友(删除数据库，再删除环信)
            messagesService.delete(likeUserId);
        }
    }

    /**
     * 通过性别和距离搜索附近得人
     *
     * @param gender
     * @param distance
     * @return
     */
    public List<NearUserVo> searchNearUser(String gender, String distance) {


        //1、查询地理位置表获取附近的人并获取附近用户的id
        List<Long> ids = userLocationApi.searchNearUser(UserHolder.getUserId(), Double.valueOf(distance));
        if (CollUtil.isEmpty(ids)) {
            return new ArrayList<>();
        }

        //2、如果查询到通过附近人用户id获取用户信息(并且按照性别排除数据)
        RecommendUserDto recommendUserDto = new RecommendUserDto();
        recommendUserDto.setGender(gender);
        Map<Long, UserInfo> map = userInfoApi.selectByIds(ids,recommendUserDto);
        //3、封装vo对象返回
        List<NearUserVo> vos = new ArrayList<>();
        for (Long id : ids) {
            //排除自己的id
            if (id == UserHolder.getUserId()) {
                continue;
            }
            if (map.get(id) != null) {
                NearUserVo vo = NearUserVo.init(map.get(id));
                vos.add(vo);
            }
        }

        return vos;
    }
}
